using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using DslEBCLangCustomSerializer.Contract;
using EBC.EBCLangDesigner;
using EnvDTE;
using EnvDTE80;

namespace DslEBCLangCustomSerializer
{
    public class CustomSerializer : ICustomSerializer
    {
        public void Initialize()
        {

        }

        public void Save(string fileName, Board board)
        {

            var path = new FileInfo(fileName).DirectoryName;
            var shortFileName = Path.GetFileNameWithoutExtension(fileName);
            var ebcFileName = path + "\\" + shortFileName + ".ebc.xml";

            var xDoc = new XDocument();

            var xBoard = CreateBoardElement(board, xDoc, shortFileName);

            //<using namespace="System.Collections"/>
            CreateUsingXElements(board, xBoard);            

            //create wire XElements
            foreach (var connection in FindConnections(board))
            {
                var wireXElement = new XElement("wire");

                //from Attribute
                var fromAttributeValue = "";
                if (connection.SourceElement is Part)
                {
                    var partElement = ((Part)connection.SourceElement);
                    if (!String.IsNullOrEmpty(partElement.PartType))
                    {
                        //from="(Join<int, int> MyPart).SomePin"
                        fromAttributeValue = String.Format("({0}) {1}.{2}", partElement.PartType, partElement.Name.Replace(" ", "_"), connection.OutputName.Replace(" ", "_"));
                    }
                    else
                        fromAttributeValue = partElement.Name.Replace(" ", "_") + "." + connection.OutputName.Replace(" ", "_");
                }
                else
                {
                    fromAttributeValue = "this." + connection.OutputName.Replace(" ", "_");
                }

                //to Attribute
                var toAttributeValue = "";
                if (connection.TargetElement is Part)
                {
                    var partElement = ((Part)connection.TargetElement);
                    if (!String.IsNullOrEmpty(partElement.PartType))
                    {
                        //to="(Join<int, int> MyPart).SomePin"
                        toAttributeValue = String.Format("({0}) {1}.{2}", partElement.PartType, partElement.Name.Replace(" ", "_"), connection.InputName.Replace(" ", "_"));
                    }
                    else
                        toAttributeValue = partElement.Name.Replace(" ", "_") + "." + connection.InputName.Replace(" ", "_");
                }
                else
                {
                    toAttributeValue = "this." + connection.InputName.Replace(" ", "_");
                }

                //type attribute
                var typeAttributeValue = connection.Type.Replace("(", "").Replace(")", "");


                var fromAttribute = new XAttribute("from", fromAttributeValue);
                var toAttribute = new XAttribute("to", toAttributeValue);
                var typeAttribute = new XAttribute("type", typeAttributeValue);


                wireXElement.Add(fromAttribute);
                wireXElement.Add(typeAttribute);
                wireXElement.Add(toAttribute);

                xBoard.Add(wireXElement);
            }
            xDoc.Save(ebcFileName);

            AddItemToProject(shortFileName, ebcFileName);


        }

        private void CreateUsingXElements(Board board, XElement xBoard)
        {
            if (!String.IsNullOrEmpty(board.Usings))
            {
                var splitUsingsProperty = board.Usings.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var s in splitUsingsProperty)
                {
                    var usingXElement = new XElement("using");                    
                    usingXElement.Add(new XAttribute("namespace", s));
                    xBoard.Add(usingXElement);
                }
            }
        }

        private IEnumerable<ElementReferencesTargetElement> FindConnections(Board board)
        {
            return board.Partition.ElementDirectory.AllElements.OfType<ElementReferencesTargetElement>();
        }

        private XElement CreateBoardElement(Board board, XDocument xDoc, string defaultName)
        {
            var xBoard = new XElement("board");
            if (board.Name == String.Empty)
                board.Name = defaultName;

            xBoard.Add(new XAttribute("name", board.Name.Replace(" ", "_")));

            if (!String.IsNullOrEmpty(board.Implements))
                xBoard.Add(new XAttribute("implements", board.Implements));

            xDoc.Add(xBoard);
            return xBoard;
        }

        private void AddItemToProject(string shortFileName, string ebcFileName)
        {
            Project addToProject = null;
            var dte = (DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.10.0");

            foreach (Project project in dte.ActiveSolutionProjects)
            {
                foreach (ProjectItem projectItem in project.ProjectItems)
                {
                    var name = projectItem.Name;
                    if (name == shortFileName + ".ebc")
                    {
                        if (name != shortFileName + ".ebc.xml")
                            addToProject = project;
                    }
                }
            }
            if (addToProject != null)
                addToProject.ProjectItems.AddFromFile(ebcFileName);
        }
    }
}